from fastapi import FastAPI, APIRouter, Request
from json.decoder import JSONDecodeError
import sys

sys.path.append('..')
from utils import log as F
from utils import sport as sport_F
from utils import user as user_F
from utils import user_tag as tag_F
from utils.http_response import HttpResponse
from utils.db import engine
from collections import defaultdict
import numpy as np
import traceback

router = APIRouter(prefix="/log", tags=["log"])


@router.post("/get_one")
async def get_one(req: Request):
    resp = HttpResponse(code=200, message="success", data=[])
    try:
        body = await req.json()
    except Exception as JSONDecodeError:
        resp.message = "json decode error"
        resp.code = 500
        return resp
    resp.data = F.select_by_col(engine, body)
    return resp


@router.post("/update_one")
async def update_one(req: Request):
    resp = HttpResponse(code=200, message="success", data=[])
    try:
        body = await req.json()
    except Exception as JSONDecodeError:
        resp.message = "json decode error"
        resp.code = 500
        return resp
    resp.data = F.update_one(engine, body["cond"], body["item"])
    return resp


@router.post("/delete_one")
async def delete_one(req: Request):
    resp = HttpResponse(code=200, message="success", data=[])
    try:
        body = await req.json()
    except Exception as JSONDecodeError:
        resp.message = "json decode error"
        resp.code = 500
        return resp
    resp.data = F.delete_one(engine, body)
    return resp


@router.post("/add_one")
async def add_one(req: Request):
    resp = HttpResponse(code=200, message="success", data=[])
    try:
        body = await req.json()
    except Exception as JSONDecodeError:
        resp.message = "json decode error"
        resp.code = 500
        return resp
    resp.data = F.add_one(engine, body)
    return resp

def find_cate(sport_id, sports):
    for sport in sports:
        if sport_id == sport['id']:
            return sport['cate']
@router.post("/recommend")
async def recommend(req: Request):
    resp = HttpResponse(code=200, message="success", data=[])
    try:
        payload = await req.json()
        all = F.select_all(engine)
        userId = payload['user_id']
        ss_dict = defaultdict(list)
        sports = sport_F.select_all(engine)
        cates = set()
        for sport in sports:
            ss_dict[sport['cate']].append(sport)
            cates.add(sport['cate'])
        cates = list(cates)
        target = [item for item in all if item['user_id'] == int(userId)]
        cnt = defaultdict(int)
        for item in target:
            cnt[find_cate(item['sport_id'], sports)] += 1
        for k in cates:
            cnt[k] +=1
        total = sum(cnt.values())
        prob = defaultdict(float)
        for k in cates:
             prob[k]= cnt[k]/total
        probs = list(prob.values())
        probs = np.array(probs)
        probs /= np.sum(probs)
        items = list(prob.keys())
        choose = []
        for i in range(3):
            res = np.random.choice(items,p=probs)
            choose.append(np.random.choice(ss_dict[res]))
        # ids = set([item['id'] for item in choose])
        v= {}
        o = []
        for ch in choose:
            if v.get(ch['id'],None) is None:
                v[ch['id']] = 1
                o.append(ch)
            else:
                pass 


        resp.data = o
        return resp
    except Exception as e:
        print(e)
        resp.message = e
        resp.code = 500
        return resp

def cos_similarity(vec1, vec2):
    """
    Compute cosine similarity between two vectors.

    Parameters:
    vec1 (numpy.ndarray): First vector.
    vec2 (numpy.ndarray): Second vector.

    Returns:
    float: Cosine similarity between the two vectors.
    """
    dot_product = np.dot(vec1, vec2)
    norm_vec1 = np.linalg.norm(vec1)
    norm_vec2 = np.linalg.norm(vec2)
    if norm_vec1 * norm_vec2 == 0:
        return 0
    similarity = dot_product / (norm_vec1 * norm_vec2) 
    return similarity



@router.post("/recommend2")
async def recommend(req: Request):
    resp = HttpResponse(code=200, message="success", data=[])
    try:
        payload = await req.json()
        all = F.select_all(engine)
        userId = payload['user_id']
        ss_dict = defaultdict(list)
        sports = sport_F.select_all(engine)
        cates = set()
        sport_cate = dict()
        for sport in sports:
            ss_dict[sport['cate']].append(sport)
            cates.add(sport['cate'])
            sport_cate[sport['id']] = sport['cate']
        cates = list(cates)

        vis = dict()
        
        for log in all:
            if vis.get(log['user_id'],None) is not None:
                temp = vis[log['user_id']]
                category = sport_cate.get(log['sport_id'])
                idx = cates.index(category)
                temp[idx] = temp[idx] + 1
                vis[log['user_id']] = temp
            else:
                temp= [0] * len(cates)
                category = sport_cate.get(log['sport_id'])
                idx = cates.index(category)
                temp[idx] = 1
                vis[log['user_id']] = temp


        others= []
        target = None
        _ids = []
        for k,v in vis.items():
            if int(k) != int(userId):
                others.append(v)
                _ids.append(int(k))
            else:
                target = np.array(v)
        
        if len(others) ==0:
            resp.data =[]
            return resp 
        sims = []
        print(target)
        for i,o in zip(_ids,others):
            vec = np.array(o)
            if i == 15 or i == 33 or i==20:
                print(f"===={i}",o)
            s = cos_similarity(vec,target)
            sims.append(s)
        # print(_ids)
        # print(sims)
        print(list(zip(_ids,sims)))
        max_idx = sims.index(max(sims))
        sim_id = [ item for item in  list(vis.keys()) if item != userId][max_idx]
        his = []
        his_target = []
        for log in all:
            if int(log['user_id']) == int(sim_id):
                his.append(log["sport_id"])
            elif int(log["user_id"]) == int(userId):
                his_target.append(log["sport_id"])
        his = set(his)
        his_target = set(his_target)
        user = user_F.select_by_col(engine,{'id':sim_id})
        tags = tag_F.select_by_col(engine, {'user_id':sim_id})
        user[0]['tags'] = tags[0]['tags'] if len(tags) >0 else []
        # print(sim_id,others)
        # # his = his -his_target
        # data = [item for item in  sports if item['id'] in his ]
        resp.data = user
        return resp
    except Exception as e:
        e.with_traceback(traceback.extract_stack())
        resp.message = e
        resp.code = 500
        return resp
